// pages/es6/es6.js
Page({

    /**
     * 页面的初始数据
     */
    data: {
        searchKey: '',
        timer: '',
        previous: 0,
    },

    /**
     * 生命周期函数--监听页面加载
     */
    onLoad: function (options) {
        // this.timeout(1000).then((value)=>{
        //     console.warn(value);
        // })
        // new Promise((resolve,reject)=>{
        //     resolve(1);
        //     console.log(2);
        // }).then(r=>{
        //     console.log(r);
        // })

        // this.testPromise();
        // this.asyncPromise();


    },
    onShow() {
        // this.forOf();
        // this.forOfIteratorForOf();

        // this.generator();
        // this.asyncTest();
        // this.asyncAwait();

        // this.changeAB();
        //对象属性复制
        this.assignTest();

        //-----降维数组-----
        // self.splitArray();
        // self.flatArray();
        // var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
        // var arr2 = self.forEachArray(arr1);
        // console.warn(arr2);
        // self.splitTest();
        // self.reduceTest();
        // self.reduceArray1();
        // self.reduceArrayTest();
    },


    //对象属性复制
    assignTest() {
        var target = { a: 1, b: 1 };
        var source1 = { b: 2, c: 2 };
        var source2 = { c: 3 };//后面的属性会覆盖前面的属性
        Object.assign(target, source1, source2);
        console.log(target);//{a: 1, b: 2, c: 3}

    },

    //通过变量解构交换两个变量的值
    changeAB() {
        let [a, b] = [10, 20];
        [a, b] = [b, a];
        console.log(a, b);//20 10
    },


    //async函数自带执行器,表示函数里有异步操作，await表示紧跟在后面的表达式需要等待结果,
    //async函数的返回值是 Promise,可以用then方法
    asyncTest() {
        //async 函数是什么？一句话，它就是 Generator 函数的语法糖
        //一比较就会发现， async 函数就是将 Generator 函数的星号（ * ）替换成 async ，将 yield 替换成 await
        // async 函数对 Generator 函数的改进，体现在以下四点
        // 1.Generator 函数的执行必须靠执行器，所以才有了 co 模块，而 async 函数自带执行器。
        // 2.async 和 await ，比起星号和 yield ，语义更清楚了。 async 表示函数里有异步操作， await 表示紧跟在后面的表达式需要等待结果
        // 3.co 模块约定， yield 命令后面只能是 Thunk 函数或 Promise 对象，而 async 函数的 await 命令后面，可以是 Promise 对象和原始类型的值
        // 4.async 函数的返回值是 Promise 对象，这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用 then 方法

        async function timeout(ms) {
            await new Promise((resolve) => {
                setTimeout(resolve, ms);
            })
        }

        async function asyncPrint(value, ms) {
            await timeout(ms);
            console.warn(value);
        }
        asyncPrint('hello world', 1000); //1秒后执行

        //设置了返回参数
        async function f() {
            return 'hello async';
        }
        f().then(v => console.warn(v));//同步函数立即执行，调用then获取后面的返回参数

        async function f1() {
            throw new Error('出错了'); //模拟抛出错误异常信息
        }
        f1().then(
            v => console.log(v),
            e => console.log(e)
        )
    },

    generator() {
        function* helloWorldGenerator() {
            yield 'hello';
            yield 'world';
            return 'ending';
        }
        var hw = helloWorldGenerator();
        // 调用遍历器对象的 next 方法，
        console.warn(hw.next());

        //Generator 函数可以不用 yield 表达式，这时就变成了一个单纯的暂缓执行函数。
        function* f() {
            console.warn('f done');
        }
        //2秒后执行f()函数
        var generator = f();
        setTimeout(function () {
            generator.next()
        }, 2000);

        //for...of 循环可以自动遍历 Generator 函数时生成的 Iterator 对象，且此时不再需要调用 next 方法
        function* foo() {
            yield 1;
            yield 2;
            yield 3;
            yield 4;
            return 6;
        }
        for (let v of foo()) {
            console.warn(v);
        }
        // 1 2 3 4 

        //利用 for...of 循环，可以写出遍历任意对象（object）的方法。原生的 JavaScript 对象没有遍历接口，无法使用 for...of 循环，
        // 通过 Generator 函数为它加上这个接口，就可以用了
        function* objectEntries(obj) {
            let proKeys = Reflect.ownKeys(obj);
            for (let proKey of proKeys) {
                yield [proKey, obj[proKey]];
            }
        }
        var jane = {
            first: 'Jane',
            last: 'Doe'
        };
        for (let [key, value] of objectEntries(jane)) {
            console.warn(`${key}: ${value}`);
        }

        // 另一种写法是，将 Generator 函数加到对象的 Symbol.iterator 属性上面
        function* objectEntriest() {
            let propKeys = Object.keys(this);
            for (let propKey of propKeys) {
                yield [propKey, this[propKey]];
            }
        }
        var jane = {
            first: 'Jane',
            last: 'Doe'
        };
        jane[Symbol.iterator] = objectEntriest;
        for (let [key, value] of jane) {
            console.log(`${key}: ${value}`);
        }

        // yield * 命令可以很方便地取出嵌套数组的所有成员
        function* iterTree(tree) {
            if (Array.isArray(tree)) {
                for (let i = 0; i < tree.length; i++) {
                    yield* iterTree(tree[i]);
                }
            } else {
                yield tree;
            }
        }
        var tree = ['a', ['b', 'c'],
            ['d', 'e']
        ];
        for (let x of iterTree(tree)) {
            console.warn(x);
        }
        // a
        // b
        // c
        // d
        // e
    },

    //for...of循环遍历数组
    forOf() {
        // for...of循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象、Generator 对象以及字符串。
        //遍历字符串
        var str1 = "agen666";
        for (let s1 of str1) {
            console.warn(s1);
        }

        //遍历数组
        for (let value of arr1) {
            console.log(value);//3个月
        }
        //遍历数组对比for...in
        var arr1 = ['3个月', '6个月', '9个月', '12个月'];
        for (let index in arr1) {
            console.log(index, arr1[index]);//0 3个月
        }

        //for...of循环支持 ES6 迭代(通过 iterables 和 iterators)和解构。。通过数组的ES6新方法enteries()结合解构，可以代替数组实例的forEach方法。
        for (var [index, item] of arr1.entries()) {
            console.log('index=' + index, 'item=', item);
        };
        //遍历数组对象
        var arr2 = [{ id: 3, name: '3个月' }, { id: 6, name: '6个月' }, { id: 9, name: '9个月' }, { id: 12, name: '12个月' },];
        for (var [index, elem] of arr2.entries()) {
            console.log('index=' + index, 'elem=');
            console.log(elem);
        };
    },


    // for...in 循环可以遍历键名。for...of结合Object.keys遍历对象
    forOfIteratorForOf() {
        let es6 = {
            edition: 6,
            committee: "TC39",
            standard: "ECMA-262"
        };
        for (let e in es6) {
            console.log(e);
        }
        // edition
        // committee
        // standard
        // for (let e of es6) {
        //     console.log(e);
        // }
        // TypeError: es6[Symbol.iterator] is not a function
        // 上面代码表示，对于普通的对象， for...in 循环可以遍历键名， for...of 循环会报错。

        // 1一种解决方法是，使用 Object.keys 方法将对象的键名生成一个数组，然后遍历这个数组。
        for (var key of Object.keys(es6)) {
            console.log(key + ':' + es6[key]);
        }

        // for...of 循环相比上面几种做法，有一些显著的优点
        // 有着同 for...in 一样的简洁语法，但是没有 for...in 那些缺点。
        // 不同于 forEach 方法，它可以与 break 、 continue 和 return 配合使用。
        // 提供了遍历所有数据结构的统一操作接口。
    },

    testPromise() {
        // 回一个 Promise 对象，如果该对象状态变为 resolved ，则会调用 then 方法指定的回调函数；
        // 如果异步操作抛出错误，状态就会变为 rejected ，就会调用 catch 方法指定的回调函数，处理这个错误。另外， then 方法指定的回调函数，如果运行中抛出错误，也会被 catch 方法捕获。
        var promise = new Promise(function (resolve, reject) {
            throw new Error('test');
        });
        promise.catch(function (error) {
            console.log(error);
        });
        //，可以发现 reject 方法的作用，等同于抛出错误
        var promise = new Promise(function (resolve, reject) {
            try {
                throw new Error('test two');
            } catch (e) {
                reject(e);
            }
        });
        promise.catch(function (error) {
            console.log(error);
        })

        //一般总是建议，Promise 对象后面要跟 catch 方法，这样可以处理 Promise 内部发生的错误。 catch 方法返回的还是一个 Promise 对象，因此后面还可以接着调用 then 方法。
        var someAsyncThing = function () {
            return new Promise(function (resolve, reject) {
                // 下面一行会报错，因为x没有声明
                resolve(x + 2);
            })
        };
        someAsyncThing()
            .catch(function (error) {
                console.log('oh no', error);
            })
            .then(function () {
                console.log('carry on');
            })
        // oh no [ReferenceError: x is not defined]
        // carry on
        // 上面代码运行完 catch 方法指定的回调函数，会接着运行后面那个 then 方法指定的回调函数。如果没有报错，则会跳过 catch 方法
        var someAsyncThing = function () {
            return new Promise(function (resolve, reject) {
                // 下面一行会报错，因为x没有声明
                resolve();
            })
        };
        // catch 方法之中，还能再抛出错误。
        someAsyncThing()
            .catch(function (error) {
                console.log('oh no', error);
            })
            .then(function () {
                console.log('carry on');
                y + 2;
            }).catch(function (error) {
                console.log('carry on', error);
            });
        // carry on
        // 上面代码中，第二个 catch 方法用来捕获，前一个 catch 方法抛出的错误。
    },

    //Promise同步函数和异步函数执行顺序问题
    normalPromise() {
        // 写法有一个缺点，就是如果 f 是同步函数，那么它会在本轮事件循环的末尾执行
        var f = () => console.log('now');
        Promise.resolve().then(f);
        console.log('next');
        // next
        // now
    },

    //有一种方法，让同步函数同步执行，异步函数异步执行，并且让它们具有统一的 API 呢？回答是可以的，并且还有两种写法。第一种写法是用async 函数来写。
    asyncPromise() {
        var f = () => console.log('now');
        (async () => f())()
            .then();
        console.log('next');
        // now
        // next
    },

    //async+await实现同步函数同步执行，异步函数异步执行
    async asyncAwait() {
        console.log(0);
        let self = this;
        await new Promise(resolve => {
            console.log(1); //同步函数
            setTimeout(() => { //异步函数
                console.log(3);
            }, 0);
            resolve();
        });
        console.log(2); //执行顺序0123
    },

    timeout(ms) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, ms, 'done')
        })
    },


    //0-----数组字符串化
    splitArray() {
        var arr = [[222, 333, 444], [55, 66, 77]]
        arr += '';
        console.log(arr);//222,333,444,55,66,77]
        arr = arr.split(',');
        console.log(arr); // ["222", "333", "444", "55", "66", "77"]
    },

    //1-----flat降维数组
    flatArray() {
        var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
        var arr2 = arr1.flat(Infinity);
        console.log(arr2);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
    },

    //2-----forEach降维数组
    forEachArray(arr1) {
        var ret = [];
        function getArray(arr1) {
            arr1.forEach((item) => {
                item instanceof Array ? getArray(item) : ret.push(item);
            })
        }
        getArray(arr1);
        return ret;
    },

    //3-----split降维数组
    splitTest() {
        // 不使用递归，使用 stack 无限反嵌套多层嵌套数组
        var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
        function flatten(input) {
            const stack = [...input];
            const res = [];
            while (stack.length) {
                // 使用 pop 从 stack 中取出并移除值
                const next = stack.pop();
                if (Array.isArray(next)) {
                    // 使用 push 送回内层数组中的元素，不会改动原始输入 original input
                    stack.push(...next);
                } else {
                    res.push(next);
                }
            }
            // 使用 reverse 恢复原数组的顺序
            return res.reverse();
        }
        var arr2 = flatten(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
        console.log(arr2);
    },

    //reduce测试
    reduceTest() {
        const array1 = [1, 2, 3, 4];
        const reducer = (accumulator, currentValue) => accumulator + currentValue;

        // 1 + 2 + 3 + 4
        console.log(array1.reduce(reducer));
        // expected output: 10

        // 5 + 1 + 2 + 3 + 4
        console.log(array1.reduce(reducer, 5));
        // expected output: 15
        console.log(array1.reduce(reducer, []));//1234

        var arr1 = [1, 2, 3];
        var arr2 = arr1.reduce((acc, cur) => acc + cur, {});
        console.warn(arr2);//[object Object]123
        //巧用concat+[]拼接数组
        var arr2 = arr1.reduce((acc, cur) => acc.concat(cur), []);
        console.warn(arr2);//[1, 2, 3]
        // 将二维数组转化为一维
        var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
            (acc, cur) => acc.concat(cur),
            []
        );
        console.warn(flattened);//[1, 2, 3]
    },

    //4-----reduce降维数组
    reduceArray1() {
        // 使用 reduce、concat 和递归无限反嵌套多层嵌套的数组
        var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
        function reduceDeep(arr1) {
            return arr1.reduce((acc, cur) => Array.isArray(cur) ? acc.concat(reduceDeep(cur)) : acc.concat(cur), []);

        }
        var arr2 = reduceDeep(arr1);
        console.warn(arr2);        // [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
    },


    //使用 reduce、concat 和递归无限反嵌套多层嵌套的数组
    reduceArray(arr1) {
        function reduceDeep(arr1) {
            return arr1.reduce((acc, cur) => Array.isArray(cur) ? acc.concat(reduceDeep(cur)) : acc.concat(cur), []);
        }
        var arr2 = reduceDeep(arr1);
        // console.warn(arr2);        // [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
        return arr2;
    },

    reduceArrayTest() {
        var self = this;
        var arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
        var arr2 = self.reduceArray(arr1);
        console.warn(arr2);
    },


})